package com.easychat.service.impl;

import java.io.File;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import com.easychat.entity.config.AppConfig;
import com.easychat.entity.constants.Constants;
import com.easychat.entity.dto.MessageSendDto;
import com.easychat.entity.dto.SysSettingDto;
import com.easychat.entity.dto.TokenUserInfoDto;
import com.easychat.entity.enums.*;
import com.easychat.entity.po.ChatSession;
import com.easychat.entity.po.ChatSessionUser;
import com.easychat.entity.po.UserContact;
import com.easychat.entity.query.*;
import com.easychat.exception.BusinessException;
import com.easychat.mappers.ChatSessionMapper;
import com.easychat.mappers.ChatSessionUserMapper;
import com.easychat.mappers.UserContactMapper;
import com.easychat.redis.RedisCache;
import com.easychat.redis.RedisComponent;
import com.easychat.utils.CopyTools;
import com.easychat.utils.DateUtil;
import com.easychat.websocket.netty.MessageHandler;
import jodd.util.ArraysUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.easychat.entity.po.ChatMessage;
import com.easychat.entity.vo.PaginationResultVO;
import com.easychat.mappers.ChatMessageMapper;
import com.easychat.service.ChatMessageService;
import com.easychat.utils.StringTools;
import org.springframework.web.multipart.MultipartFile;


/**
 * 聊天消息表 业务接口实现
 */
@Service("chatMessageService")
public class ChatMessageServiceImpl implements ChatMessageService {

	@Resource
	private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;
	@Resource
	private ChatSessionMapper<ChatSession, ChatSessionQuery> chatSessionMapper;
	@Resource
	private RedisComponent redisComponent;
	@Resource
	private MessageHandler messageHandler;
	@Resource
	private AppConfig appConfig;
	@Resource
	private UserContactMapper<UserContact,UserContactQuery> userContactMapper;
	@Resource
	private RedisCache redis;
	private static final Logger logger = LoggerFactory.getLogger(ChatMessageServiceImpl.class);

	/**
	 * 根据条件查询列表
	 */
	@Override
	public List<ChatMessage> findListByParam(ChatMessageQuery param) {
		return this.chatMessageMapper.selectList(param);
	}

	/**
	 * 根据条件查询列表
	 */
	@Override
	public Integer findCountByParam(ChatMessageQuery param) {
		return this.chatMessageMapper.selectCount(param);
	}

	/**
	 * 分页查询方法
	 */
	@Override
	public PaginationResultVO<ChatMessage> findListByPage(ChatMessageQuery param) {
		int count = this.findCountByParam(param);
		int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

		SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
		param.setSimplePage(page);
		List<ChatMessage> list = this.findListByParam(param);
		PaginationResultVO<ChatMessage> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
		return result;
	}

	/**
	 * 新增
	 */
	@Override
	public Integer add(ChatMessage bean) {
		return this.chatMessageMapper.insert(bean);
	}

	/**
	 * 批量新增
	 */
	@Override
	public Integer addBatch(List<ChatMessage> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.chatMessageMapper.insertBatch(listBean);
	}

	/**
	 * 批量新增或者修改
	 */
	@Override
	public Integer addOrUpdateBatch(List<ChatMessage> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.chatMessageMapper.insertOrUpdateBatch(listBean);
	}

	/**
	 * 多条件更新
	 */
	@Override
	public Integer updateByParam(ChatMessage bean, ChatMessageQuery param) {
		StringTools.checkParam(param);
		return this.chatMessageMapper.updateByParam(bean, param);
	}

	/**
	 * 多条件删除
	 */
	@Override
	public Integer deleteByParam(ChatMessageQuery param) {
		StringTools.checkParam(param);
		return this.chatMessageMapper.deleteByParam(param);
	}

	/**
	 * 根据MessageId获取对象
	 */
	@Override
	public ChatMessage getChatMessageByMessageId(Long messageId) {
		return this.chatMessageMapper.selectByMessageId(messageId);
	}

	/**
	 * 根据MessageId修改
	 */
	@Override
	public Integer updateChatMessageByMessageId(ChatMessage bean, Long messageId) {
		return this.chatMessageMapper.updateByMessageId(bean, messageId);
	}

	/**
	 * 根据MessageId删除
	 */
	@Override
	public Integer deleteChatMessageByMessageId(Long messageId) {
		return this.chatMessageMapper.deleteByMessageId(messageId);
	}

    @Override
    public MessageSendDto saveMessage(ChatMessage chatMessage, TokenUserInfoDto tokenUserInfoDto) {
//		System.out.println(System.currentTimeMillis());

		//不是机器人发送消息，判断好友状态
		if (!Constants.RoBoT_UID.equals(tokenUserInfoDto.getUserId())) {
			List<String> contactList = redisComponent.getUserContactList(tokenUserInfoDto.getUserId());
			if (!contactList.contains(chatMessage.getContactId())) {
				UserContactTypeEnum userContactTypeEnum = UserContactTypeEnum.getByPrefix(chatMessage.getContactId());
				if (UserContactTypeEnum.USER == userContactTypeEnum) {
					throw new BusinessException(ResponseCodeEnum.CODE_902);
				} else {
					throw new BusinessException(ResponseCodeEnum.CODE_903);
				}
			}
		}
		String sessionId;

		String sendUserId = tokenUserInfoDto.getUserId();
		String contactId = chatMessage.getContactId();
		Long curTime = System.currentTimeMillis();
		UserContactTypeEnum contactTypeEnum = UserContactTypeEnum.getByPrefix(contactId);
		//生成sessionId
		if (UserContactTypeEnum.USER == contactTypeEnum) {
			sessionId =StringTools.getChatSessionIdUser(new String[]{sendUserId,contactId});
		} else if (UserContactTypeEnum.GROUP == contactTypeEnum) {
			sessionId =StringTools.getChatSessionIdGroup(contactId);
		} else {
			sessionId = null;
		}

		MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByType(chatMessage.getMessageType());
		// 判断消息类型是否合法
		if(messageTypeEnum==null ||
				!ArrayUtils.contains(new Integer[]{MessageTypeEnum.CHAT.getType(),MessageTypeEnum.MEDIA_CHAT.getType()},messageTypeEnum.getType())){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		//消息状态：    发消息最初的状态 ，如果是媒体消息:发送中 如果是文本消息:已发送
		Integer status=MessageTypeEnum.MEDIA_CHAT.equals(messageTypeEnum) ? MessageStatusEnum.SENDING.getStatus() : MessageStatusEnum.SENDED.getStatus();
		//messageContent防止注入！将消息转义
		String messageContent = StringTools.cleanHtmlTag(chatMessage.getMessageContent());

		//todo  异步存储
		chatMessage.setSessionId(sessionId);
		chatMessage.setSendTime(curTime);
		chatMessage.setContactType(contactTypeEnum.getType());
		chatMessage.setStatus(status);
		chatMessage.setMessageContent(messageContent);
		chatMessage.setSendUserId(tokenUserInfoDto.getUserId());
		chatMessage.setSendUserNickName(tokenUserInfoDto.getNickName());
//		new Thread(()->{
//			chatMessageMapper.insert(chatMessage);
//		});
		chatMessageMapper.insert(chatMessage);


		//更新chat session 表
		ChatSession chatSession = new ChatSession();
		chatSession.setLastMessage(messageContent);
		if(contactTypeEnum== UserContactTypeEnum.GROUP){//如果是群，最后一条消息展示为 xxx:巴拉巴拉
			chatSession.setLastMessage(tokenUserInfoDto.getNickName()+":"+chatMessage.getMessageContent());
		}
		//todo 异步
//		new Thread(()->{
//			chatSessionMapper.updateBySessionId(chatSession,sessionId);
//		});
		chatSessionMapper.updateBySessionId(chatSession,sessionId);

		MessageSendDto messageSendDto = CopyTools.copy(chatMessage, MessageSendDto.class);

		//如果是和机器人对话，即发消息的人的对方是机器人
		if (Constants.RoBoT_UID.equals(contactId)){
			SysSettingDto sysSettingDto = redisComponent.getSysSettingDto();
			//机器人的token类
			TokenUserInfoDto robotTokenUserInfoDto = new TokenUserInfoDto();
			robotTokenUserInfoDto.setUserId(sysSettingDto.getRobotUid());
			robotTokenUserInfoDto.setNickName(sysSettingDto.getRobotNickName());
			//机器人的消息类
			ChatMessage robotChatMessage = new ChatMessage();
			robotChatMessage.setSendUserId(Constants.RoBoT_UID);
			robotChatMessage.setContactId(sendUserId);
			robotChatMessage.setMessageContent("我只是机器人，听不懂你在说什么");//这里可以对接AI，实现回复
			robotChatMessage.setMessageType(MessageTypeEnum.CHAT.getType());
			saveMessage(robotChatMessage,robotTokenUserInfoDto);
		}else {
			//发送消息给对方
			messageHandler.sendMessage(messageSendDto);
		}
//		System.out.println(System.currentTimeMillis());
		return messageSendDto;
    }

	@Override
	public void saveMessageFile(String userId, Long messageId, MultipartFile file, MultipartFile cover) {
		//健壮性校验 防止绕过前端操作
		ChatMessage message = chatMessageMapper.selectByMessageId(messageId);
		if (null == message) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		if (!message.getSendUserId().equals(userId)) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		SysSettingDto sysSettingDto = redisComponent.getSysSettingDto();
		String fileSuffix = StringTools.getFileSuffix(file.getOriginalFilename());
		if (!StringTools.isEmpty(fileSuffix) && ArraysUtil.contains(Constants.IMAGE_SUFFIX_LIST, fileSuffix.toLowerCase())//图片
				&& file.getSize() > Constants.FILE_SIZE_MB * sysSettingDto.getMaxImageSize()) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}else if (!StringTools.isEmpty(fileSuffix) && ArraysUtil.contains(Constants.VIDEO_SUFFIX_LIST, fileSuffix.toLowerCase())//视频
				&& file.getSize() > Constants.FILE_SIZE_MB * sysSettingDto.getMaxVideoSize()) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}else if (!StringTools.isEmpty(fileSuffix) &&//其他 文件
				!ArraysUtil.contains(Constants.VIDEO_SUFFIX_LIST, fileSuffix.toLowerCase()) &&
				!ArraysUtil.contains(Constants.IMAGE_SUFFIX_LIST, fileSuffix.toLowerCase()) &&
				file.getSize() > Constants.FILE_SIZE_MB * sysSettingDto.getMaxFileSize()) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		String fileName = file.getOriginalFilename();//文件原始名
		String fileExtName = StringTools.getFileSuffix(fileName);//文件名后缀
		String fileRealName = messageId + fileExtName;//真实文件名
		//文件夹名按年月来分类
		String month= DateUtil.format(new Date(message.getSendTime()),DateTimePatternEnum.YYYYMM.getPattern());
		//文件夹名
		File folder = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + month);
		if (!folder.exists()){
			folder.mkdirs();
		}
		File uploadFile = new File(folder.getPath() + "/" + fileRealName);
		try {
			file.transferTo(uploadFile);
			if (cover != null) {
				cover.transferTo(new File(uploadFile.getPath() + Constants.COVER_IMAGE_SUFFIX));
			}
		} catch (Exception e) {
			logger.error("上传文件失败", e);
			throw new BusinessException("文件上传失败");
		}
		//更新ChatMessage的状态，因为 非文字 刚发送时状态为 0”正在发送“
		ChatMessage updateInfo = new ChatMessage();
		updateInfo.setStatus(MessageStatusEnum.SENDED.getStatus());
		ChatMessageQuery messageQuery = new ChatMessageQuery();
		messageQuery.setMessageId(messageId);
		chatMessageMapper.updateByParam(updateInfo, messageQuery);

		MessageSendDto messageSend = new MessageSendDto();
		messageSend.setStatus(MessageStatusEnum.SENDED.getStatus());
		messageSend.setMessageId(message.getMessageId());
		messageSend.setMessageType(MessageTypeEnum.FILE_UPLOAD.getType());
		messageSend.setContactId(message.getContactId());
		messageHandler.sendMessage(messageSend);


	}

	@Override
	public File downloadFile(TokenUserInfoDto tokenUserInfo, Long fileId, Boolean showCover) {
		ChatMessage chatMessage = chatMessageMapper.selectByMessageId(fileId);
		String contactId = chatMessage.getContactId();
		UserContactTypeEnum userContactTypeEnum = UserContactTypeEnum.getByPrefix(contactId);
		if (UserContactTypeEnum.USER==userContactTypeEnum &&
		!tokenUserInfo.getUserId().equals(contactId)){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		if (UserContactTypeEnum.GROUP==userContactTypeEnum){
			//检查是否在群里，不在群里不能下载图片
			UserContactQuery userContactQuery=new UserContactQuery();
			userContactQuery.setStatus(UserContactStatusEnum.FRIEND.getCode());
			userContactQuery.setUserId(tokenUserInfo.getUserId());
			userContactQuery.setContactId(contactId);
			userContactQuery.setContactType(UserContactTypeEnum.GROUP.getType());

			Integer count = userContactMapper.selectCount(userContactQuery);
			if (count==0){
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}
		}

		String month = DateUtil.format(new Date(chatMessage.getSendTime()), DateTimePatternEnum.YYYYMM.getPattern());
		File folder = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + month);
		if (!folder.exists()) {
			folder.mkdirs();
		}
		String fileName = chatMessage.getFileName();
		String fileExtName = StringTools.getFileSuffix(fileName);
		String fileRealName = fileId + fileExtName;

		if (showCover != null && showCover) {
			fileRealName = fileRealName + Constants.COVER_IMAGE_SUFFIX;
		}
		File file = new File(folder.getPath() + "/" + fileRealName);
		if (!file.exists()) {
			logger.info("文件不存在");
			throw new BusinessException(ResponseCodeEnum.CODE_602);
		}
		return file;
	}
}